#include <iostream>
#include <thread>
#include <vector>
#include <string>
#include <chrono>
#include <ctime>
#include <json.hpp>

#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <semaphore.h>
#include <atomic>

#include "user.hpp"
#include "group.hpp"
#include "public.hpp"

using namespace std;
using json = nlohmann::json;

// 记录当前系统登入的用户信息
User g_currentUser;
// 记录当前登入用户的好友列表信息
vector<User> g_currentUserFriendList;
// 记录当前登入用户的群组列表信息
vector<Group> g_currentUserGroupList;
// 控制聊天主页面
bool isMainMenuRunning = false;
// 用于读写线程之间的通信
sem_t rwsem;
// 记录登入操作的状态
atomic_bool g_isLoginSuccess{false};
// 显示当前登入用户的信息
void showCurrentUserData();

// 接收数据的线程
void readTaskHandler(int clientfd);
// 获取系统时间
string getCurrentTime();
// 主聊天页面
void mainMenu(int clientfd);

int main(int argc, char **argv)
{
    if (argc < 3)
    {
        cerr << "command invalid! example: ./ChatClient 127.0.0.1 8000" << endl;
        exit(-1);
    }
    // 解析ip和port
    char *ip = argv[1];
    uint16_t port = atoi(argv[2]);

    // 创建client的socket
    int clientfd = socket(AF_INET, SOCK_STREAM, 0);
    if (clientfd == -1)
    {
        cerr << "socket create error" << endl;
        exit(-1);
    }
    // 构建server信息
    sockaddr_in server;
    memset(&server, 0, sizeof(sockaddr_in));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(ip);
    server.sin_port = htons(port);

    // 连接client和server
    if (connect(clientfd, (sockaddr *)&server, sizeof(sockaddr_in)) == -1)
    {
        cerr << "connect server error" << endl;
        close(clientfd);
        exit(-1);
    }
    // 初始化读写线程通信的信号量
    sem_init(&rwsem, 0, 0);
    // 连接服务器成功后，启动接收线程负责接收数据
    std::thread readTask(readTaskHandler, clientfd);
    // 设置分离线程
    readTask.detach();
    for (;;)
    {
        // 显示首页菜单
        cout << "======================================" << endl;
        cout << "1. login" << endl;
        cout << "2. register" << endl;
        cout << "3. quit" << endl;
        cout << "======================================" << endl;
        cout << "please enter a choice: ";
        int choice = 0;
        cin >> choice;
        // 取出缓冲区内的回车
        cin.get();
        switch (choice)
        {
        // login
        case 1:
        {
            int id = 0;
            char pwd[50] = {0};
            cout << "userid: ";
            cin >> id;
            cin.get();
            cout << "password: ";
            cin.getline(pwd, 50);

            json js;
            js["msgid"] = LOGIN_MSG;
            js["id"] = id;
            js["password"] = pwd;
            string request = js.dump();
            g_isLoginSuccess = false;
            int len = send(clientfd, request.c_str(), strlen(request.c_str()) + 1, 0);
            if (len == -1)
            {
                cerr << "send login message error" << endl;
            }
            // 等待信号量，子线程处理完登入时服务器返回的数据包后，唤醒这里
            sem_wait(&rwsem);
            if (g_isLoginSuccess)
            {
                isMainMenuRunning = true;
                mainMenu(clientfd);
            }
            break;
        }

        // register
        case 2:
        {
            char name[50] = {0};
            char pwd[50] = {0};
            cout << "username: ";
            cin.getline(name, 50);
            cout << "password: ";
            cin.getline(pwd, 50);

            json js;
            js["msgid"] = REG_MSG;
            js["name"] = name;
            js["password"] = pwd;
            string request = js.dump();
            int len = send(clientfd, request.c_str(), strlen(request.c_str()) + 1, 0);
            if (len == -1)
            {
                cerr << "send register message error" << endl;
            }
            //等待子线程处理完注册的消息时唤醒
            sem_wait(&rwsem);
            break;
        }
        // quit
        case 3:
        {
            close(clientfd);
            sem_destroy(&rwsem);
            return 0;
        }
        default:
            cerr << "invalid input" << endl;
            break;
        }
    }
    return 0;
}
void showCurrentUserData()
{
    cout << "===============================login user===============================" << endl;
    cout << "current login user id:" << g_currentUser.getId() << "name: " << g_currentUser.getName() << endl;
    cout << "------------------------------friend list-------------------------------" << endl;
    if (!g_currentUserFriendList.empty())
    {
        for (User &user : g_currentUserFriendList)
        {
            cout << user.getId() << " " << user.getName() << " " << user.getState() << endl;
        }
    }
    cout << "------------------------------group list-------------------------------" << endl;
    if (!g_currentUserGroupList.empty())
    {
        for (Group group : g_currentUserGroupList)
        {
            cout << group.getId() << " " << group.getName() << " " << group.getDesc() << endl;
            for (GroupUser &user : group.getUsers())
            {
                cout << "---" << user.getId() << " " << user.getName() << " " << user.getState()
                     << " " << user.getRole() << endl;
            }
        }
    }
    cout << "=================================END====================================" << endl;
}
// 处理登入时服务器返回的数据包
void doLoginResponse(json &responseJs)
{

    if (!responseJs["error"].get<int>() == 0)
    {
        // 登入失败
        cerr << responseJs["errmsg"] << endl;
        g_isLoginSuccess = false;
    }
    else
    {
        // 登入成功,在客户端记录ta的id和name
        g_currentUser.setId(responseJs["id"].get<int>());
        g_currentUser.setName(responseJs["name"]);

        // 记录好友列表
        if (responseJs.contains("friends"))
        {
            g_currentUserFriendList.clear();
            vector<string> vec = responseJs["friends"];
            for (string &str : vec)
            {
                json js = json::parse(str);
                User user;
                user.setId(js["id"].get<int>());
                user.setName(js["name"]);
                user.setState(js["state"]);
                g_currentUserFriendList.push_back(user);
            }
        }
        // 记录当前用户的群组列表信息
        if (responseJs.contains("groups"))
        {
            g_currentUserGroupList.clear();
            vector<string> vec = responseJs["groups"];
            for (string &groupstr : vec)
            {
                json js = json::parse(groupstr);
                Group group;
                group.setId(js["id"].get<int>());
                group.setName(js["groupname"]);
                group.setDesc(js["groupdesc"]);
                vector<string> vec2 = js["users"];
                for (string &userstr : vec2)
                {
                    GroupUser user;
                    json js2 = json::parse(userstr);
                    user.setId(js2["id"].get<int>());
                    user.setName(js2["name"]);
                    user.setState(js2["state"]);
                    user.setRole(js2["role"]);
                    group.getUsers().push_back(user);
                }

                g_currentUserGroupList.push_back(group);
            }
        }

        // 显示登入用户的基本信息
        showCurrentUserData();

        // 显示当前登入用户的离线消息
        if (responseJs.contains("offlinemsg"))
        {
            vector<string> vec = responseJs["offlinemsg"];
            for (string &str : vec)
            {
                json js = json::parse(str);
                if (js["msgid"].get<int>() == ONE_CHAT_MEG)
                {
                    cout << js["time"].get<string>() << "[" << js["id"] << "]" << js["name"].get<string>()
                         << "  said: " << js["msg"].get<string>() << endl;
                }
                if (js["msgid"].get<int>() == GROUP_CHAT_MSG)
                {
                    cout << "群消息[" << js["groupid"] << "]" << js["time"].get<string>()
                         << "[" << js["id"] << "]" << js["name"].get<string>()
                         << "  said: " << js["msg"].get<string>() << endl;
                }
            }
        }
        g_isLoginSuccess = true;
    }
}
// 处理注册时服务器返回的消息包
void doRegResponse(json &responseJs)
{
    if (!responseJs["error"].get<int>() == 0)
    {
        cerr << "user is already exist,register error" << endl;
    }
    else
    {
        cout<< "user register success, userid is" << responseJs["id"] << endl;
    }
}
// 处理服务器传过来的消息包
void readTaskHandler(int clientfd)
{
    for (;;)
    {
        char buffer[1024]={0};
        auto len = recv(clientfd, buffer, 1024, 0); // 退出后在这里阻塞，返回-1
        if (len == -1 || len == 0)
        {
            close(clientfd);
            exit(0);
        }
        // 接收ChatServer转发的数据，反序列化
        json js = json::parse(buffer);
        if (js["msgid"].get<int>() == ONE_CHAT_MEG)
        {
            cout << js["time"].get<string>() << "[" << js["id"] << "]" << js["name"].get<string>() << ": " << js["msg"].get<string>() << endl;
            continue;
        }
        if (js["msgid"].get<int>() == GROUP_CHAT_MSG)
        {
            cout << "群消息[" << js["groupid"] << "]" << js["time"].get<string>()
                 << "[" << js["id"] << "]" << js["name"].get<string>()
                 << ": " << js["msg"].get<string>() << endl;
            continue;
        }
        if (js["msgid"].get<int>() == LOGIN_MSG_ACK)
        {
            // 处理登入
            doLoginResponse(js);
            // 通知主线程，登入返回的消息包处理完成
            sem_post(&rwsem);
            continue;
        }
        if (js["msgid"].get<int>() == REG_MSG_ACK)
        {
            doRegResponse(js);
            // 通知主线程，登入返回的消息包处理完成
            sem_post(&rwsem);
            continue;
        }
    }
}

string getCurrentTime()
{
    auto tt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    struct tm *ptm = localtime(&tt);
    char date[60] = {0};
    sprintf(date, "%d-%02d-%02d %02d:%02d:%02d",
            (int)ptm->tm_year + 1900, (int)ptm->tm_mon + 1, (int)ptm->tm_mday,
            (int)ptm->tm_hour, (int)ptm->tm_min, (int)ptm->tm_sec);
    return std::string(date);
}

// int类型传递socketfd，string传递的是用户输入的数据

//"help" command handler,打印commandmap
void help(int fd = 0, string str = "");
// "chat" command handler
void chat(int, string);
// "addfriend" command handler
void addfriend(int, string);
// "creategroup" command handler
void creategroup(int, string);
// "addgroup" command handler
void addgroup(int, string);
// "groupchat" command handler
void groupchat(int, string);
// "loginout" command handler
void loginout(int, string);
// 系统支持的客户端命令列表
unordered_map<string, string> commandMap = {
    {"help", "显示所有支持的命令,格式help"},
    {"chat", "一对一聊天,格式chat:friendid:message"},
    {"addfriend", "添加好友,格式addfriend:friendid"},
    {"creategroup", "创建群组,格式creategroup:groupname:groupdesc"},
    {"addgroup", "加入群组,格式addgroup:groupid"},
    {"groupchat", "群聊,格式groupchat:groupid:message"},
    {"loginout", "注销,格式loginout"}};

// 注册系统支持的客户端命令处理
unordered_map<string, function<void(int, string)>> commandHandlerMap = {
    {"help", help},
    {"chat", chat},
    {"addfriend", addfriend},
    {"creategroup", creategroup},
    {"addgroup", addgroup},
    {"groupchat", groupchat},
    {"loginout", loginout}};
void mainMenu(int clientfd)
{
    help();
    char buffer[1024] = {0};
    while (isMainMenuRunning)
    {
        cin.getline(buffer, 1024);
        string commandbuf(buffer);
        // 存储命令
        string command;
        int idx = commandbuf.find_first_of(":");
        if (idx == -1)
        {
            // 处理help和loginout命令
            command = commandbuf;
        }
        else
        {
            // 其他命令
            command = commandbuf.substr(0, idx);
        }
        auto p = commandHandlerMap.find(command);
        if (p == commandHandlerMap.end())
        {
            // 没有找到对应的处理函数
            cerr << "invalid command input" << endl;
            continue;
        }
        // 提取出对应消息的处理函数，并传入用户输入命令的消息部分
        p->second(clientfd, commandbuf.substr(idx + 1, commandbuf.size() - idx));
    }
}

void help(int fd, string str)
{
    cout << "show command list" << endl;
    for (auto &p : commandMap)
    {
        cout << p.first << ": " << p.second << endl;
    }
    cout << endl;
}

void chat(int clientfd, string str)
{
    int idx = str.find_first_of(":");
    if (idx == -1)
    {
        cerr << "chat command invalid" << endl;
    }
    int friendid = atoi((str.substr(0, idx)).c_str());
    string message = str.substr(idx + 1, str.size() - idx);
    json js;
    js["msgid"] = ONE_CHAT_MEG;
    js["id"] = g_currentUser.getId();
    js["name"] = g_currentUser.getName();
    js["toid"] = friendid;
    js["msg"] = message;
    js["time"] = getCurrentTime();
    string buffer = js.dump();
    int len = send(clientfd, buffer.c_str(), strlen(buffer.c_str()) + 1, 0);
    if (len == -1)
    {
        cerr << "send chat Message error: " << message << endl;
    }
}

void addfriend(int clientfd, string str)
{
    int friendid = atoi(str.c_str());
    int userid = g_currentUser.getId();

    json js;
    js["msgid"] = ADD_FRIEND_MSG;
    js["id"] = userid;
    js["friendid"] = friendid;
    string buffer = js.dump();
    int len = send(clientfd, buffer.c_str(), strlen(buffer.c_str()) + 1, 0);
    if (len == -1)
    {
        cerr << "send addFriend Message error" << endl;
    }
}

void creategroup(int clientfd, string str)
{
    int idx = str.find_first_of(":");
    if (idx == -1)
    {
        cerr << "createGroup command invalid" << endl;
        return;
    }
    string groupName = str.substr(0, idx);
    string groupDesc = str.substr(idx + 1, str.size() - idx);
    json js;
    js["msgid"] = CREATE_GROUP_MSG;
    js["id"] = g_currentUser.getId();
    js["groupname"] = groupName;
    js["groupdesc"] = groupDesc;
    string buffer = js.dump();
    int len = send(clientfd, buffer.c_str(), strlen(buffer.c_str()) + 1, 0);
    if (len == -1)
    {
        cerr << "createGroup Message error: " << endl;
    }
}

void addgroup(int clientfd, string str)
{
    int groupid = atoi(str.c_str());
    int userid = g_currentUser.getId();

    json js;
    js["msgid"] = ADD_GROUP_MSG;
    js["id"] = userid;
    js["groupid"] = groupid;
    string buffer = js.dump();
    int len = send(clientfd, buffer.c_str(), strlen(buffer.c_str()) + 1, 0);
    if (len == -1)
    {
        cerr << "addGroup Message error" << endl;
    }
}

void groupchat(int clientfd, string str)
{
    int idx = str.find_first_of(":");
    if (idx == -1)
    {
        cerr << "chat command invalid" << endl;
    }
    int groupid = atoi((str.substr(0, idx)).c_str());
    string message = str.substr(idx + 1, str.size() - idx);
    json js;
    js["msgid"] = GROUP_CHAT_MSG;
    js["id"] = g_currentUser.getId();
    js["name"] = g_currentUser.getName();
    js["groupid"] = groupid;
    js["msg"] = message;
    js["time"] = getCurrentTime();
    string buffer = js.dump();
    int len = send(clientfd, buffer.c_str(), strlen(buffer.c_str()) + 1, 0);
    if (len == -1)
    {
        cerr << "send Groupchat Message error: " << message << endl;
    }
}

void loginout(int clientfd, string str)
{
    json js;
    js["msgid"] = LOGIN_OUT_MSG;
    js["id"] = g_currentUser.getId();
    string buffer = js.dump();
    int len = send(clientfd, buffer.c_str(), strlen(buffer.c_str()) + 1, 0);
    if (len == -1)
    {
        cerr << "send loginout message error" << endl;
    }
    cout << "login out" << endl;
    isMainMenuRunning = false;
}
